added SSCLI 1.0
[windows-sources.git] / shared source / sscli20 / tools / nmake / ifexpr.cpp
blobe083cb6b137f01592d083dcb4b29e458c71a817a
1 // ==++==
2 //
3 //
4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
5 //
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
10 //
11 // You must not remove this notice, or any other, from this software.
12 //
14 // ==--==
15 // IFEXPR.C -- routines to handle directives
17 // Purpose:
18 // Module contains routines to handle !directives. This module is transparent to
19 // rest of NMAKE. It also contains lgetc() used by lexer.c
21 #include "precomp.h"
22 #ifdef _MSC_VER
23 #pragma hdrstop
24 #endif
26 // function prototypes
28 void skipToNextDirective(void);
29 void processIfs(char*, UCHAR);
30 UCHAR ifsPresent(char*, unsigned, char**);
31 void processCmdSwitches(char*);
32 char * readInOneLine(void);
33 char * getDirType(char*, UCHAR*);
35 // macros that deal w/ the if/else directives' stack
37 #define ifStkTop() (ifStack[ifTop])
38 #define popIfStk() ifTop--
39 #define pushIfStk(A) (ifStack[++ifTop] = A)
41 #define INCLUDE 0x09
42 #define CMDSWITCHES 0x0A
43 #define ERROR 0x0B
44 #define MESSAGE 0x0C
45 #define UNDEF 0x0D
48 #ifdef _MBCS
50 // GetTxtChr : get the next character from a text file stream
52 // This routine handles mixed DBCS and ASCII characters as
53 // follows:
55 // 1. The second byte of a DBCS character is returned in a
56 // word with the high byte set to the lead byte of the character.
57 // Thus the return value can be used in comparisions with
58 // ASCII constants without being mistakenly matched.
60 // 2. A DBCS space character (0x8140) is returned as two
61 // ASCII spaces (0x20). I.e. return a space the 1st and 2nd
62 // times we're called.
64 // 3. ASCII characters and lead bytes of DBCS characters
65 // are returned in the low byte of a word with the high byte
66 // set to 0.
68 int GetTxtChr(FILE *bs)
70 extern int chBuf; // Character buffer
71 int next; // The next byte
72 int next2; // The one after that
74 // -1 in chBuf means it doesn't contain a valid character
76 // If we're not in the middle of a double-byte character,
77 // get the next byte and process it.
79 if(chBuf == -1) {
80 next = getc(bs);
81 // If this byte is a lead byte, get the following byte
82 // and store both as a word in chBuf.
84 if (_ismbblead(next)) {
85 next2 = getc(bs);
86 chBuf = (next << 8) | next2;
87 // If the pair matches a DBCS space, set the return value
88 // to ASCII space.
90 if(chBuf == 0x8140)
91 next = 0x20;
93 } else {
94 // Else we're in the middle of a double-byte character.
96 if(chBuf == 0x8140) {
97 // If this is the 2nd byte of a DBCS space, set the return
98 // value to ASCII space.
100 next = 0x20;
101 } else {
102 // Else set the return value to the whole DBCS character
104 next = chBuf;
106 // Reset the character buffer
107 chBuf = -1;
110 // Return the next character
111 return(next);
114 #endif // _MBCS
117 #ifdef _MBCS
129 UngetTxtChr(int c, FILE *bs)
131 extern int chBuf; // Character buffer
132 int nTrailByte; // The trail byte to put back
134 if (-1 == chBuf) { // We're not in the middle of a DB character
135 if (0 == (c >> 8)) { // CASE 1: normal character
136 c = ungetc(c, bs); // putback normal char
137 } else { // CASE 2: at trail byte (c=LBTB)
138 chBuf = c; // change chBuf is sufficient
140 } else { // CASE 3: at lead byte (c=LB, chBuf=LBTB)
141 nTrailByte = chBuf & (int)0xff; // Figure out the trail byte to putback
142 ungetc(nTrailByte, bs); // putback trail byte
143 c = ungetc(c, bs); // putback lead byte
144 chBuf = -1;
146 return (c);
149 #endif // _MBCS
151 // lgetc() local getc - handles directives and returns char
153 // arguments: init global boolean value -- TRUE if tools.ini
154 // is the file being parsed
155 // colZero global boolean value -- TRUE if at first column
157 // actions:
158 // gets a character from the currently open file.
159 // loop
160 // if it is column zero and the char is '!' or
161 // there is a previous directive to be processed do
162 // read in one line into buffer.
163 // find directive type and get a pointer to rest of
164 // text.
165 // case directive of:
167 // CMDSWITCHES : set/reset global flags
168 // ERROR : set up global error message
169 // printed by error routine on
170 // termination. (not implemented yet )
171 // INCLUDE : calls processInclude
172 // continues with new file...
173 // UNDEF : undef the macro in the table
174 // IF
175 // IFDEF
176 // IFNDEF
177 // ELSE
178 // ENDIF : change the state information
179 // on the ifStack
180 // evaluate expression if required
181 // skip text if required (and look
182 // for the next directive)
183 // ( look at processIfs() )
184 // free extra buffers used (only one buffer need be
185 // maintained )
186 // increment lexer's line count
187 // we 're now back at column zero
188 // get next char from current file
189 // end if
190 // end loop
191 // return a char
193 // returns : a character (that is not part of any directive...)
195 // modifies: ifStack if directives' stack, static to this module
196 // ifTop index of current element at top of stack
197 // line lexer's line count...
199 // file current file, if !include is found...
200 // fName if !include is processed...
203 lgetc()
205 UCHAR dirType;
206 int c;
207 char *s, *t;
208 MACRODEF *m;
210 for (c = GetTxtChr(file); prevDirPtr || (colZero && (c == '!'));
211 ++line, c = GetTxtChr(file)) {
212 colZero = FALSE; // we saw a '!' incolZero
213 if (!prevDirPtr) {
214 s = readInOneLine(); // might modify lbufPtr -
215 // if input text causes realloc */
216 } else {
217 UngetTxtChr(c, file);
218 s = prevDirPtr;
219 prevDirPtr = NULL;
222 t = getDirType(s, &dirType);
224 if (dirType == INCLUDE) {
225 if (init) {
226 makeError(line, SYNTAX_UNEXPECTED_TOKEN, s);
229 // processInclude eats up first char in new file
230 // if it is space char. we check for that and break out.
232 if (processIncludeFile(t) == (UCHAR) NEWLINESPACE) {
233 c = ' '; // space character is returned
234 break; // colZero is now FALSE
237 else if (dirType == CMDSWITCHES) {
238 processCmdSwitches(t);
240 else if (dirType == ERROR) {
241 makeError(line, USER_CONTROLLED, t);
243 else if (dirType == MESSAGE) {
244 if (!_tcsnicmp(t, "\\t", 2)) {
245 printf("\t");
246 t+=2;
248 makeMessage(USER_MESSAGE, t);
250 else if (dirType == UNDEF) {
251 char *tmp;
252 tmp = _tcstok(t, " \t");
253 if (_tcstok(NULL, " \t")) {
254 makeError(line, SYNTAX_UNEXPECTED_TOKEN, tmp);
256 if (NULL != (m = findMacro(tmp))) {
257 SET(m->flags, M_UNDEFINED);
260 else processIfs(t, dirType);
261 colZero = TRUE; // finished with this directive
262 if (s != lbufPtr) // free buffer if it had expanded macros
263 FREE(s);
265 return(c); // return a character to the lexer
269 // readInOneLine()
271 // arguments: lbufPtr pointer(static/global to this module) to buffer that
272 // will hold text of line being read in
273 // lbufSize size of buffer(static/global to this module), updated
274 // if buffer is realloc'd
275 // actions : skip spaces/tabs and look for the directive.
276 // line continuations allowed in usual way
277 // if space-backslash-nl keep looking...
278 // if colZero of next line has comment char
279 // (#, or ; in tools.ini), look at next line...
280 // if first non-space char is '\n' or EOF report
281 // fatal-error and stop.
283 // keep reading in chars and storing in the buffer until
284 // a newline, EOF or a '#' which is NOT in column
285 // zero is seen
286 // if comment char in column zero ('#' or ';' in tools.ini)
287 // skip the line, continue with text on next line.
288 // if buffer needs to be realloc'd increase size by
289 // MAXBUF, a global constant.
290 // if newline was found, eat up newline.
291 // null terminate string for return.
292 // if '#' was found discard chars till the a newline or EOF.
293 // if EOF was found, push it back on stream for return
294 // to the lexer the next time.
296 // now expand macros. get a different buffer with clean
297 // text after expansion of macros.
299 // modifies : colZero global boolean value ( thru' call to
300 // skipBackSlash())
301 // lbufPtr buffer pointer, in case of reallocs.
302 // lbufSize size of buffer, increased if buffer is realloc'd
303 // Note: the buffer size will grow to be just greater than the size
304 // of the longest directive in any of the files processed,
305 // if it calls for any realloc's
306 // Do NOT process ESCH here. It is processed at a higher level.
308 // returns : pointer to buffer.
311 char *
312 readInOneLine()
314 int c;
315 unsigned index = 0;
316 register char *s;
318 if (((c = skipWhiteSpace(FROMSTREAM)) == '\n') || (c == EOF))
319 makeError(line, SYNTAX_MISSING_DIRECTIVE);
321 UngetTxtChr(c, file);
323 for (;;) {
324 c = GetTxtChr(file);
325 c = skipBackSlash(c, FROMSTREAM);
326 if (c == '#' || c == '\n' || c == EOF) {
327 break;
329 if ((index+2) > lbufSize) {
330 lbufSize += MAXBUF;
331 if (!lbufPtr) {
332 lbufPtr = (char *) allocate(lbufSize+1); // +1 for NULL byte
333 } else {
334 lbufPtr = (char *) REALLOC(lbufPtr, lbufSize+1);
335 if (!lbufPtr)
336 makeError(line, MACRO_TOO_LONG);
339 *(lbufPtr + (index++)) = (char) c;
341 *(lbufPtr + index) = '\0'; // null terminate the string
342 if (c == '#') {
343 for(c = GetTxtChr(file); (c != '\n') && (c != EOF); c = GetTxtChr(file))
345 // newline at end is eaten up
348 if (c == EOF) {
349 UngetTxtChr(c, file); // this directive is to be processed
352 s = lbufPtr; // start expanding macros here
353 s = removeMacros(s); // remove and expand macros in string s
354 return(s);
358 // getDirType()
360 // arguments: s - pointer to buffer that has directive text.
361 // dirType - pointer to unsigned char that gets set
362 // with directive type.
364 // actions : goes past directive keyword, sets the type code and
365 // returns a pointer to rest of test.
367 char *
368 getDirType(
369 char *s,
370 UCHAR *dirType
373 char *t;
374 int len;
376 *dirType = 0;
377 for (t = s; *t && !WHITESPACE(*t); ++t);
378 len = (int) (t - s); // store len of directive
379 while (*t && WHITESPACE(*t)) {
380 ++t; // go past directive keyword
381 } if (!_tcsnicmp(s, "INCLUDE", 7) && (len == 7)) {
382 *dirType = INCLUDE;
383 } else if (!_tcsnicmp(s, "CMDSWITCHES", 11) && (len == 11)) {
384 *dirType = CMDSWITCHES;
385 } else if (!_tcsnicmp(s, "ERROR", 5) && (len == 5)) {
386 *dirType = ERROR;
387 } else if (!_tcsnicmp(s, "MESSAGE", 7) && (len == 7)) {
388 *dirType = MESSAGE;
389 } else if (!_tcsnicmp(s, "UNDEF", 5) && (len == 5)) {
390 *dirType = UNDEF;
391 } else {
392 *dirType = ifsPresent(s, len, &t) ; // directive one of "if"s?
395 if (!*dirType) {
396 makeError(line, SYNTAX_BAD_DIRECTIVE, lbufPtr);
398 return(t);
402 // processCmdSwitches() -- processes command line switches in makefiles
404 // arguments: t pointer to flag settings specified.
406 // actions : sets or resets global flags as specified in the directive.
407 // The allowed flags are:
408 // s - silent mode, d - debug output (dates printed)
409 // n - no execute mode, i - ignore error returns from commands
410 // u - dump inline files
411 // If parsing tools.ini, can also handle epqtbc
412 // reports a bad directive error for any other flags
413 // specified
415 // modifies : nothing
417 // returns : nothing
419 void
420 processCmdSwitches(
421 char *t // pointer to switch values
424 for (; *t; ++t) { // ignore errors in flags specified
425 switch (*t) {
426 case '+':
427 while (*++t && *t != '-') {
428 if (_tcschr("DINSU", (unsigned short)_totupper(*t))) {
429 setFlags(*t, TRUE);
430 } else if (init && _tcschr("ABCEKLPQRTY", (unsigned short)_totupper(*t))) {
431 setFlags(*t, TRUE);
432 } else {
433 makeError(line, SYNTAX_BAD_CMDSWITCHES);
437 if (!*t) {
438 break;
441 case '-':
442 while (*++t && *t != '+') {
443 if (_tcschr("DINSU", (unsigned short)_totupper(*t))) {
444 setFlags(*t, FALSE);
445 } else if (init && _tcschr("ABCEKLMPQRTV", (unsigned short)_totupper(*t))) {
446 setFlags(*t, FALSE);
447 } else {
448 makeError(line, SYNTAX_BAD_CMDSWITCHES);
451 break;
453 default:
454 if (!WHITESPACE(*t)) {
455 makeError(line, SYNTAX_BAD_CMDSWITCHES);
457 break;
459 if (!*t) {
460 break;
465 // ifsPresent() -- checks if current directive is one of the "if"s
467 // arguments: s pointer to buffer with directive name in it
468 // len length of the directive that was seen
469 // t pointer to address upto which processed
471 // actions : does a string compare in the buffer for one of the
472 // directive keywords. If string matches true, it returns
473 // a non-zero value, the code for the specific directive
475 // modifies : nothing
477 // returns : a zero if no match, or the code for directive found.
479 UCHAR
480 ifsPresent(
481 char *s,
482 unsigned len,
483 char **t
486 UCHAR ifFlags = 0; // takes non-zero value when one of
487 // if/else etc is to be processed
489 if (!_tcsnicmp(s, "IF", 2) && (len == 2)) {
490 ifFlags = IF_TYPE;
491 } else if (!_tcsnicmp(s, "IFDEF", 5) && (len == 5)) {
492 ifFlags = IFDEF_TYPE;
493 } else if (!_tcsnicmp(s, "IFNDEF", 6) && (len == 6)) {
494 ifFlags = IFNDEF_TYPE;
495 } else if (!_tcsnicmp(s, "ELSE", 4) && (len == 4)) {
496 // 'else' or 'else if' or 'else ifdef' or 'else ifndef'
497 char *p = *t;
499 if (!*p) {
500 ifFlags = ELSE_TYPE;
501 } else {
502 for (s = p; *p && !WHITESPACE(*p); p++)
504 len = (unsigned) (p - s);
505 while (*p && WHITESPACE(*p)) {
506 p++;
508 *t = p;
509 if (!_tcsnicmp(s, "IF", 2) && (len == 2)) {
510 ifFlags = ELSE_IF_TYPE;
511 } else if (!_tcsnicmp(s, "IFDEF", 5) && (len == 5)) {
512 ifFlags = ELSE_IFDEF_TYPE;
513 } else if (!_tcsnicmp(s, "IFNDEF", 6) && (len == 6)) {
514 ifFlags = ELSE_IFNDEF_TYPE;
518 else if (!_tcsnicmp(s, "ELSEIF", 6) && (len == 6)) {
519 ifFlags = ELSE_IF_TYPE;
521 else if (!_tcsnicmp(s, "ELSEIFDEF", 9) && (len == 9)) {
522 ifFlags = ELSE_IFDEF_TYPE;
524 else if (!_tcsnicmp(s, "ELSEIFNDEF", 10) && (len == 10)) {
525 ifFlags = ELSE_IFNDEF_TYPE;
527 else if (!_tcsnicmp(s, "ENDIF", 5) && (len == 5)) {
528 ifFlags = ENDIF_TYPE;
531 return(ifFlags);
535 // processIfs() -- sets up / changes state information on "if"s
537 // arguments: s pointer to "if" expression ( don't care
538 // for "endif" )
540 // kind code indicating if processing if/else/ifdef etc.
542 // actions : modifies a stack (ifStack) by pushing/popping or
543 // sets/resets bits in the top element on the
544 // stack(examining the previous element pushed if
545 // required).
546 // case (kind) of
547 // IF
548 // IFDEF
549 // IFNDEF
550 // IF defined() : if no more space on ifStack
551 // (too many nesting levels) abort...
552 // set IFELSE bit in elt.
553 // push elt on ifStack.
554 // if more than one elt on stack
555 // and outer level "ifelse" false
556 // set IGNORE bit, skipToNextDirective
557 // else
558 // evaluate expression of
559 // current "if"
560 // if expr true set CONDITION bit in elt
561 // else skipToNextDirective.
562 // ELSE : if no elt on stack or previous
563 // directive was "else", flag error, abort
564 // clear IFELSE bit in elt on stack.
565 // if current ifelse block is to
566 // be skipped (IGNORE bit is on
567 // in outer level if/else),skip...
568 // else FLIP condition bit.
569 // if "else" part is false
570 // skipToNextDirective.
571 // ENDIF : if no elt on stack, flag error,abort
572 // pop an elt from ifStack.
573 // if there are elts on stack
574 // and we are in a "false" block
575 // skipToNextDirective.
576 // end case
578 // modifies: ifStack if directives' stack, static to this module
579 // ifTop index of current element at top of stack
580 // line lexer's line count (thru calls to
581 // skipToNextDirective())
583 // returns : nothing
585 void
586 processIfs(
587 char *s,
588 UCHAR kind
591 UCHAR element; // has its bits set and is pushed on the ifStack
593 switch (kind) {
594 case IF_TYPE:
595 case IFDEF_TYPE:
596 case IFNDEF_TYPE:
597 if (ifTop == IFSTACKSIZE-1) {
598 makeError(line, SYNTAX_TOO_MANY_IFS);
600 element = (UCHAR) 0;
601 SET(element, NMIFELSE);
602 pushIfStk(element);
603 if (ifTop && OFF(ifStack[ifTop-1], NMCONDITION)) {
604 SET(ifStkTop(), NMIGNORE);
605 skipToNextDirective();
606 } else if (evalExpr(s, kind)) {
607 SET(ifStkTop(), NMCONDITION);
608 } else {
609 skipToNextDirective();
611 break;
613 case ELSE_TYPE:
614 if ((ifTop < 0) || (OFF(ifStkTop(), NMIFELSE) && OFF(ifStkTop(), NMELSEIF))) {
615 makeError(line, SYNTAX_UNEXPECTED_ELSE);
617 CLEAR(ifStkTop(), NMIFELSE);
618 CLEAR(ifStkTop(), NMELSEIF);
619 if (ON(ifStkTop(), NMIGNORE)) {
620 skipToNextDirective();
621 } else {
622 FLIP(ifStkTop(), NMCONDITION);
623 if (OFF(ifStkTop(), NMCONDITION)) {
624 skipToNextDirective();
627 break;
629 case ELSE_IF_TYPE:
630 case ELSE_IFDEF_TYPE:
631 case ELSE_IFNDEF_TYPE:
632 if ((ifTop < 0) || (OFF(ifStkTop(), NMIFELSE) && OFF(ifStkTop(), NMELSEIF))) {
633 makeError(line, SYNTAX_UNEXPECTED_ELSE);
635 CLEAR(ifStkTop(), NMIFELSE);
636 SET(ifStkTop(), NMELSEIF);
637 if (ON(ifStkTop(), NMIGNORE)) {
638 skipToNextDirective();
639 } else {
640 if (ON(ifStkTop(), NMCONDITION)) {
641 SET(ifStkTop(), NMIGNORE);
642 CLEAR(ifStkTop(), NMCONDITION);
643 skipToNextDirective();
644 } else if (evalExpr(s, kind)) {
645 SET(ifStkTop(), NMCONDITION);
646 } else {
647 skipToNextDirective();
650 break;
652 case ENDIF_TYPE:
653 if (ifTop < 0) {
654 makeError(line, SYNTAX_UNEXPECTED_ENDIF);
656 popIfStk();
657 if (ifTop >= 0) {
658 if (OFF(ifStkTop(), NMCONDITION)) {
659 skipToNextDirective();
663 default:
664 break; // default should never happen
669 // skipToNextDirective() -- skips to next line that has '!' in column zero
671 // actions : gets first char of the line to be skipped if it is
672 // not a directive ( has no '!' on column zero ).
673 // a "line" that is skipped may in fact span many
674 // lines ( by using sp-backslash-nl to continue...)
675 // comments in colZero are skipped as part of the previous
676 // line ('#' or ';' in tools.ini)
677 // comment char '#' elsewhere in line implies the end of
678 // that line (with the next newline / EOF)
679 // if a '!' is found in colZero, read in the next directive
680 // if the directive is NOT one of if/ifdef/ifndef/else/
681 // endif, keep skipping more lines and look for the
682 // next directive ( go to top of the routine here ).
683 // if EOF found before next directive, report error.
685 // modifies : line global lexer line count
687 // returns : nothing
689 void
690 skipToNextDirective()
692 register int c;
693 UCHAR type;
695 repeat:
697 for (c = GetTxtChr(file); (c != '!') && (c != EOF) ;c = GetTxtChr(file)) {
698 ++line; // lexer's line count
700 do {
701 if (c == '\\') {
702 c = skipBackSlash(c, FROMSTREAM);
703 if (c == '!' && colZero) {
704 break;
705 } else {
706 colZero = FALSE;
709 if ((c == '#') || (c == '\n') || (c == EOF)) {
710 break;
712 c = GetTxtChr(file);
713 } while (TRUE);
715 if (c == '#') {
716 for (c = GetTxtChr(file); (c != '\n') && (c != EOF); c = GetTxtChr(file))
719 if ((c == EOF) || (c == '!')) {
720 break;
724 if (c == '!') {
725 if (prevDirPtr && (prevDirPtr != lbufPtr)) {
726 FREE(prevDirPtr);
728 prevDirPtr = readInOneLine();
729 getDirType(prevDirPtr, &type);
730 if (type > ENDIF_TYPE) { // type is NOT one of the "if"s
731 ++line;
732 goto repeat;
734 } else if (c == EOF) {
735 makeError(line, SYNTAX_EOF_NO_DIRECTIVE);